<?php

class ShowPager
{
    const DB = 1;
    const REDIS = 2;

    private $data_provider;
    private $pager;

    function __construct($dao, $curr_page = 1, $per_page = 10, $type = self::DB)
    {
        if ($type == self::DB)
            $this->data_provider = new PagerDBDataProvider($dao);
        else if ($type == self::REDIS)
            $this->data_provider = new PagerRedisDataProvider($dao);
        else
            return false;

        $this->pager = new Pager($per_page, $curr_page);
    }

    public function setTotalCount($cmd, $params = array())
    {
        if (is_numeric($cmd))
            $this->pager->setTotalCount($cmd);
        else
            $this->pager->setTotalCount($this->data_provider->getCount($cmd, $params));
        return $this;
    }

    // alias function of setTotalCount
    public function ct($cmd, $params = array())
    {
        return $this->setTotalCount($cmd, $params);
    }

    public function getResult($cmd, $params = array())
    {
        if (is_string($cmd))
            $rs = $this->data_provider->getResult($cmd, $this->pager->getPerPage(), ($this->pager->getCurrPage() - 1) * $this->pager->getPerPage(), $params);
        else if (is_array($cmd))
            $rs = $cmd;
        return array('pager'=>$this->pager, 'rs'=>$rs);
    }

    // alias function of getResult
    public function rs($cmd, $params = array())
    {
        return $this->getResult($cmd, $params);
    }

    public function getPager()
    {
        return $this->pager;
    }

    function __call($method, $arguments)
    {
        if (method_exists($this->pager, $method))
        {
            if ($arguments)
            {
                return $this->pager->$method($arguments[0]);
            }
            else
            {
                return $this->pager->$method();
            }
        }
        else
        {
            return false;
        }
    }
}

abstract class PagerDataProvider
{
    protected $dao;

    function __construct($dao)
    {
        $this->dao = $dao;
    }
    abstract protected function getCount($cmd, $params = array());
    abstract protected function getResult($cmd, $per_page, $offset, $params = array());
}
class PagerDbDataProvider extends PagerDataProvider
{
    public function getCount($count_sql, $params = array())
    {
        if (is_array($this->dao))
        {
            return $this->dao['ct']->getOne($count_sql, $params);
        }
        else
        {
            return $this->dao->getOne($count_sql, $params);
        }
    }

    public function getResult($select_sql, $per_page, $offset, $params = array())
    {
        if (is_array($this->dao))
        {
            return $this->dao['select']->selectLimit($select_sql, $per_page, $offset, $params);
        }
        else
        {
            return $this->dao->selectLimit($select_sql, $per_page, $offset, $params);
        }
    }
}
class PagerRedisDataProvider extends PagerDataProvider
{
    public function getCount($set_key, $params = array())
    {
        return $this->dao->sCard($set_key);
    }

    public function getResult($key, $per_page, $offset, $params = array())
    {
        $fields = array();
        if (isset($params['get']))
        {
            foreach ((array)$params['get'] as $get)
            {
                if (false === ($pos = strpos($get, '->')))
                {
                    $fields[] = $get;
                }
                else
                {
                    $fields[] = substr($get, $pos + 2);
                }
            }
        }
        $fields_count = count($fields);

        $redis_rs = array();
        if (isset($params['store']))
        {
            $redis_rs = $this->dao->lRange($params['store'], 0, $per_page * $fields_count);
        }

        if (! $redis_rs)
        {
            if (isset($params['expire']))
            {
                $expire = $params['expire'];
                unset($params['expire']);
            }
            $params = array_merge($params, array('limit'=>array($offset, $per_page)));

            $redis_rs = $this->dao->sort($key, $params);

            if (isset($params['store']))
            {
                $redis_rs = $this->dao->lRange($params['store'], 0, $per_page * $fields_count);
                $this->dao->expire($params['store'], $expire);
            }
        }

        $rs = $row = array();
        if ($fields_count)
        {
            foreach ($redis_rs as $k => $v)
            {
                $i = $k % $fields_count;
                $row[$fields[$i]] = $v;
                if ($i == ($fields_count - 1))
                {
                    $rs[] = $row;
                    $row = array();
                }
            }
            return $rs;
        }
        else
        {
            return $redis_rs;
        }
    }
}

class Pager
{
    public $total_count;
    public $per_page;
    public $curr_page;
    public $total_page = null;
    public $nearer_page = 3;

    function __construct($per_page = 10, $curr_page = 1)
    {
        $this->per_page = $per_page;
        $this->curr_page = $curr_page;
        $this->total_count = 0;
    }

    public function getTotalCount()
    {
        return $this->total_count;
    }

    public function setTotalCount($total_count)
    {
        $this->total_count = $total_count;
    }

    public function setNearerPage($offset){
        $this->nearer_page = $offset;
    }

    public function getPerPage()
    {
        return $this->per_page;
    }

    public function setPerPage($per_page)
    {
        $this->per_page = $per_page;
    }

    public function getCurrPage()
    {
        return $this->curr_page;
    }

    public function setCurrPage($curr_page)
    {
        $this->curr_page = $curr_page;
    }

    public function getTotalPage()
    {
        return $this->total_page ? $this->total_page : ceil($this->getTotalCount() / $this->getPerPage());
    }

    public function setTotalPage($total_page)
    {
        $this->total_page = $total_page;
    }

    public function hasFirstPage()
    {
        return $this->getCurrPage() != 1;
    }

    public function hasLastPage()
    {
        return $this->getCurrPage() != $this->getTotalPage();
    }

    public function hasPrePage()
    {
        return $this->getCurrPage() > 1;
    }

    public function hasNextPage()
    {
        return $this->getCurrPage() < $this->getTotalPage();
    }

    public function getNearerPages()
    {
        $min_page = (($this->getCurrPage() - $this->nearer_page) > 0 ) ? $this->getCurrPage() - $this->nearer_page : 1;
        $max_page = (($this->getCurrPage() + $this->nearer_page) < $this->getTotalPage()) ? $this->getCurrPage() + $this->nearer_page : $this->getTotalPage();

        if ($min_page <= $max_page)
            return range($min_page, $max_page);
        else
            return array();
    }
}

?>